package cn.openea.perpartner.meily.web.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.openea.perpartner.common.core.config.SystemConfig;
import cn.openea.perpartner.meily.util.CosUpload;
import cn.openea.perpartner.meily.web.entity.dto.QrCodeDto;
import cn.openea.perpartner.meily.web.entity.dto.QrCodeVo;
import cn.openea.perpartner.meily.web.service.IQrCodeService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Date;
/**
 * @author Weird077
 * @description TODO
 * @date 2022-12-28 14:42
 */
@Slf4j
@Service
public class QrCodeServiceImpl implements IQrCodeService {
    @Value("${tencent.applets.appid}")
    private String appid;
    @Value("${tencent.applets.secret}")
    private String secret;
    @Value("${tencent.applets.page}")
    private String page;
    @Value("${tencent.applets.env_version}")
    private String env_version;
    @Autowired
    private CosUpload cosUpload;
    // 默认头像地址
    private String defaultURL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/perpartner.png";
    // 引导图片地址
    private String guideURL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/qrguide.png";

    @Override
    public String getPhoneNumber(String code) {
        String wxUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + getAccessToken();
        JSONObject paramMap = new JSONObject();
        paramMap.put("code", code);
        HttpResponse rep = HttpRequest.post(wxUrl)
                .header("Content-Type", "application/json")
                .body(paramMap.toString()).execute();
        JSONObject result = JSON.parseObject(rep.body());
        JSONObject response = new JSONObject();
        if (result.get("errcode").equals(0)) {
            JSONObject data = result.getJSONObject("phone_info");
            response.put("code", "200");
            response.put("msg", "获取手机号成功");
            response.put("data", data.getString("phoneNumber"));
            return response.toString();
        }
        response.put("code", "201");
        response.put("msg", "获取手机号失败");
        return response.toString();
    }

    @Override
    public String getQrCode(QrCodeVo vo) throws IOException {
        if (StrUtil.isBlank(vo.getPage())){
            vo.setPage(page);
        }
        if (StrUtil.isBlank(vo.getEnvVersion())){
            vo.setEnvVersion(env_version);
        }
        BufferedImage qrcodeImg = generateQrCode(vo.getPage(), vo.getScene(),false,vo.getWidth(),vo.getEnvVersion());
        File uploadfile = getWriteLocalFile(RandomUtil.randomString(19), qrcodeImg);
        String url = cosUpload.uploadFile(uploadfile,false);
        emptyDir(SystemConfig.getUploadPath());
        return url;
    }

    @Override
    public String getPoster(QrCodeDto dto) throws IOException {
        if (StrUtil.isBlank(dto.getPageUrl())){
            dto.setPageUrl(page);
        }
        //生成小程序二维码
        BufferedImage qrcodeImg = generateQrCode(dto.getPageUrl(), dto.getParamArgs(),true, "", "");
        //创建分享模板图片海报
        BufferedImage shareImg = createSharePicture(dto, qrcodeImg);
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        ImageIO.write(shareImg, "jpg", outStream);
        InputStream inputStream = new ByteArrayInputStream(outStream.toByteArray());
        //将图片上传到存储桶并返回URL，这里使用文件流的方式
        String url = cosUpload.uploadFile(inputStream, dto.getUserId() + ".jpg",true);
        return url;
    }

    /**
     * 生成小程序二维码
     * @param page
     *         当前小程序相对页面 必须是已经发布的小程序存在的页面（否则报错），例如 pages/index/index,
     *         根路径前不要填加 /,不能携带参数（参数请放在scene字段里），如果不填写这个字段，默认跳主页面
     * @param scene
     *         最大32个可见字符，只支持数字，大小写英文以及部分特殊字符：!#$&'()*+,/:;=?@-._~，
     *         其它字符请自行编码为合法字符（因不支持%，中文无法使用 urlencode 处理，请使用其他编码方式）
     */
    private BufferedImage generateQrCode(String page, String scene, boolean isHyaline, String width, String envVersion) throws IOException {
        BufferedImage qrcodeImg = null;
        // 发起API请求，返回对应的参数信息
        try {
            // 获取小程序二维码的API地址，带参数
            String REQURL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken();
            //调用微信接口生成二维码
            URL url = new URL(REQURL);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("POST");// 提交模式
            // 发送POST请求必须设置如下两行
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream());
            // 小程序二维码的API请求时的参数
            JSONObject paramJson = new JSONObject();
            //这就是你二维码里携带的参数 String型  名称不可变
            paramJson.put("scene", scene);
            //注意该接口传入的是page而不是path
            paramJson.put("page", page);
            //这是设置扫描二维码后跳转的页面
            paramJson.put("width", StrUtil.isBlank(width)?200:width);
            paramJson.put("is_hyaline", isHyaline);
            paramJson.put("auto_color", true);
            paramJson.put("env_version", StrUtil.isBlank(envVersion)?env_version:envVersion);
            printWriter.write(paramJson.toString());
            // flush输出流的缓冲
            printWriter.flush();
            //开始获取数据
            BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream());
            qrcodeImg = ImageIO.read(bis);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return qrcodeImg;
    }

    /**
     * 创建分享模板图片海报
     * @param dto
     * @param qrcodeImg
     * @return
     * @throws IOException
     */
    private BufferedImage createSharePicture(QrCodeDto dto, BufferedImage qrcodeImg) throws IOException {
        BufferedImage shareImg = new BufferedImage(630, 1012, BufferedImage.TYPE_INT_RGB);
        String nickName = dto.getNickName();
        String avatarUrl = dto.getAvatarUrl();
        String dateStr = DateUtil.format(new Date(), "MM/dd HH:mm:ss");
        BufferedImage userImage;
        try{
            // 如果头像地址为空，则使用默认头像地址
            userImage = ImageIO.read(new URL(avatarUrl));
        } catch (Exception e) {
            userImage = ImageIO.read(new URL(defaultURL));
        }
        // 读入商品主图片
        BufferedImage productImage = ImageIO.read(new URL(dto.getImageUrl()));
        // 长按二维码
        BufferedImage guideImage = ImageIO.read(new URL(guideURL));

        // ****** 兼容旧版本海报生成策略 ******
        if(StrUtil.isNotBlank(dto.getType())){
            String tmpguideURL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/qrtext.png";// 商品团购引导图片地址
            // type=bmhb 就生成 帮卖海报
            if("bmhb".equalsIgnoreCase(dto.getType())){
                tmpguideURL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/qrtextbm.png";// 帮卖海报引导图片地址
            } else if("cphb".equalsIgnoreCase(dto.getType())){
                tmpguideURL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/qrtextcp.png";//测评海报引导图片地址
            } else if("tghd".equalsIgnoreCase(dto.getType()) || "yphd".equalsIgnoreCase(dto.getType())){
                tmpguideURL = "https://www-1308272144.cos.ap-guangzhou.myqcloud.com/static/image/qrtexthd.png";//参与活动引导图片地址
            }
            guideImage = ImageIO.read(new URL(tmpguideURL));
        }
        // ****** 兼容旧版本海报生成策略 ******

        // 以背景的图片做为底图
        Graphics graph = shareImg.createGraphics();
        //设置颜色
        graph.setColor(Color.WHITE);
        //将设置的颜色设置为背景色
        graph.fillRect(0, 0, 630, 1012);

        // 头像旁边的昵称信息 25，BLACK 原坐标：37，这里要重新设置字体颜色，否则和背景相同颜色会显示不出来
        graph.setColor(Color.BLACK);
        Font font = new Font("Microsoft YaHei Light", Font.CENTER_BASELINE, 23);
        graph.setFont(font);
        graph.drawString(nickName + (StrUtil.isBlank(dto.getInviteMsg())?"邀请":dto.getInviteMsg()), 117, 60);

        // 头像旁边的时间信息 20，LIGHT_GRAY  原坐标：75  微软雅黑
        graph.setColor(Color.LIGHT_GRAY);
        font = new Font("Microsoft YaHei Light", Font.CENTER_BASELINE, 18);
        graph.setFont(font);
        graph.drawString(dateStr, 117, 90);

        // 主图下的商品标题，38 BLACK  原坐标：734  , fontsize=25
        graph.setColor(Color.BLACK);
        font = new Font("Microsoft YaHei Light", Font.BOLD, 29);
        graph.setFont(font);
        // 通过JLabel来计算文本的总长度px
        JLabel jLabel = new JLabel(dto.getProductName());
        jLabel.setFont(font);
        FontMetrics fontMetrics = jLabel.getFontMetrics(font);
        int textLength = dto.getProductName().length();
        //一行的总长度，用于判断是否超出了范围
        int totalWidth = fontMetrics.stringWidth(dto.getProductName());
        //计算一行的高度
        int textHeight = fontMetrics.getHeight();
        //行间距
        int heightSpace = 5;
        int maxWidth = shareImg.getWidth()-40;
        if (totalWidth > maxWidth) {
            // 如果总长度超过了整个长度限制
            int alreadyWriteLine = 0; //已经写了多少行
            int nowWidth = 0; //目前一行写的长度
            for (int i = 0; i < textLength; i++) {
                int oneWordWidth = fontMetrics.charWidth(dto.getProductName().charAt(i)); //获取单个字符的长度
                int tempWidth = oneWordWidth + nowWidth; //判断目前的一行加上这个字符的长度是否超出了总长度
                if (tempWidth > maxWidth) {
                    // 如果超出了一行的总长度，则要换成下一行
                    nowWidth = 0;
                    alreadyWriteLine++;
                    int writeY = 744 + alreadyWriteLine * (textHeight + heightSpace);
                    graph.drawString(dto.getProductName().charAt(i) + "", 30 + nowWidth, writeY);
                    nowWidth = oneWordWidth;
                } else {
                    // 当前行长度足够，可以直接画
                    int writeY = 744 + alreadyWriteLine * (textHeight + heightSpace);
                    graph.drawString(dto.getProductName().charAt(i) + "", 30 + nowWidth, writeY);
                    nowWidth = tempWidth;
                }
            }
        } else {
            // 如果没有超过限制，直接画
            graph.drawString(dto.getProductName(), 30, 744);
        }

        graph.setColor(Color.RED);
        font = new Font("Microsoft YaHei Light", Font.BOLD, 25);
        graph.setFont(font);

        // 组装海报价格信息
        graph.drawString("￥ ", 33, 870);
        font = new Font("Microsoft YaHei Light", Font.BOLD, 38);
        graph.setFont(font);

        // ****** 兼容旧版本海报生成策略 ******
        if(StrUtil.isNotBlank(dto.getType())){
            if("tghd".equalsIgnoreCase(dto.getType())) {
                // 参与活动显示
                graph.drawString(dto.getProductPrice() + "玫丽专享价", 60, 870);
            } else if("yphd".equalsIgnoreCase(dto.getType())) {
                // type=yphd 就是邀请参与样品活动
                graph.drawString(dto.getProductPrice() + "领取样品", 60, 870);
            } else {
                // 这里除了显示团购海报外，还负责显示 测评海报 、帮卖海报
                graph.drawString(dto.getProductPrice(), 60, 870);
            }
        } else {
            graph.drawString(dto.getProductPrice() + dto.getPriceDesc(), 60, 870);
        }
        // ****** 兼容旧版本海报生成策略 ******

        // 绘制商品主图片
        graph.drawImage(productImage.getScaledInstance(580, 580, Image.SCALE_DEFAULT), 25, 130, null);
        // 绘制小程序二维码
        graph.drawImage(qrcodeImg.getScaledInstance(190, 190, Image.SCALE_DEFAULT), 425, 797, null);
        // 绘制长按二维码
        graph.drawImage(guideImage.getScaledInstance(330, 60, Image.SCALE_DEFAULT), 33, 897, null);

        // ****** 兼容旧版本海报生成策略 ******
        if(StrUtil.isBlank(dto.getType())){
            // 绘制指引信息的文字
            Color gray = new Color(146, 145, 145);
            graph.setColor(gray);
            font = new Font("Microsoft YaHei Light", Font.PLAIN, 23);
            graph.setFont(font);
            graph.drawString(dto.getGiudeText(), 40, 936);
        }
        // ****** 兼容旧版本海报生成策略 ******

        // 头像图片是一个圆型，并定位
        Ellipse2D.Double shape = new Ellipse2D.Double(25, 25, 80, 80);
        // 需要保留的区域
        graph.setClip(shape);
        // 再绘制头像到背景底图的顶层
        graph.drawImage(userImage, 25, 25, 80, 80, null);

        graph.dispose();
        return shareImg;
    }

    /**
     * 将创建的海报在本地生成文件
     * @param userid 用户id，以用户id作为文件的名字
     * @param shareImg 需要生成海报的源
     * @return 生成的文件
     */
    private File getWriteLocalFile(String userid, BufferedImage shareImg) throws IOException {
        String basePath =  SystemConfig.getUploadPath() + "/" + RandomUtil.randomNumber() + "/";
        File file = new File(basePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        String filepath = basePath + userid + ".jpg";
        file = new File(filepath);
        // 将推广图片写入本地地址
        ImageIO.write(shareImg, "jpg", file);
        return file;
    }


    /**
     * 获取小程序access_token
     **/
    private String getAccessToken() {
        String wxUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
        String result = HttpUtil.get(wxUrl);
        if (StrUtil.isNotBlank(result)) {
            JSONObject wxObject = JSON.parseObject(result);
            return wxObject.getString("access_token");
        }
        return "";
    }

    /**
     * 清空文件夹
     * @param filePath 文件夹路径
     */
    private void emptyDir(String filePath) throws IOException {
        Files.walkFileTree(Paths.get(filePath), new SimpleFileVisitor<Path>() {
                // 遍历删除文件
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Files.delete(file);
                    return FileVisitResult.CONTINUE;
                }
                // 遍历删除目录
                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    Files.delete(dir);
                    return FileVisitResult.CONTINUE;
                }
            }
        );
    }
}
